package yawning.mew.cat;

import java.util.HashMap;
import java.util.Map;

import yawning.mew.ProcEffect.Trigger;
import yawning.mew.character.CharacterModel.AttackResult;
import yawning.mew.sim.SimulationEvent;
import yawning.mew.sim.SimulationTarget;

public class EventMeleeSwing extends SimulationEvent {
	CatModelSimState mState;
	Map<SimulationTarget,MeleeStats> mDamageStats;

	class MeleeStats {
		long mWhiteDamage;
		int mNrWhiteMiss;
		int mNrWhiteDodge;
		int mNrWhiteParry;
		int mNrWhiteGlance;
		int mNrWhiteCrit;
		int mNrWhiteHit;

		long mFurySwipesDamage;
		int mNrFurySwipesMiss;
		int mNrFurySwipesDodge;
		int mNrFurySwipesParry;
		int mNrFurySwipesCrit;
		int mNrFurySwipesHit;
	}

	EventMeleeSwing(CatModelSimState state) {
		super(state.mSimEngine);
		mState = state;
		mDamageStats = new HashMap<SimulationTarget,MeleeStats>();

		for (SimulationTarget target: mState.mTargetMap.values()) {
			mDamageStats.put(target, new MeleeStats());
		}

		// I could schedule here, except I expect the script to handle the swing timer now.
	}

	@Override
	public void onTick() {
		if (mState.mActiveTarget == null) {
			stop();
			return;
		}

		AttackResult result = (mState.mActiveTargetData.mCanParry) ? mState.mModel.getFrontalWhiteResult() : mState.mModel.getWhiteResult();
		MeleeStats stats = mDamageStats.get(mState.mActiveTarget);
		if (stats == null) {
			stats = new MeleeStats();
			mDamageStats.put(mState.mActiveTarget, stats);
		}
		String targetName = mState.mActiveTarget.getTarget().getName();

		if (result == AttackResult.MISS) {
			stats.mNrWhiteMiss++;

			if (mState.mIsLogging) mState.mLog.log("White Swing: %s MISS\n", targetName);
		} else if (result == AttackResult.DODGE) {
			stats.mNrWhiteDodge++;

			if (mState.mIsLogging) mState.mLog.log("White Swing: %s DODGE\n", targetName);
		} else if (result == AttackResult.PARRY) {
			stats.mNrWhiteParry++;

			if (mState.mIsLogging) mState.mLog.log("White Swing: %s PARRY\n", targetName);
		} else {
			double damage = mState.mModel.getWeaponDamage();

			if (result == AttackResult.GLANCE) {
				stats.mNrWhiteGlance++;
				damage *= 0.75;
			} else if (result == AttackResult.CRIT) {
				stats.mNrWhiteCrit++;
				damage *= mState.mModel.multCrit;
			} else {
				stats.mNrWhiteHit++;
			}

			damage *= mState.getDirectDamageMult();
			// Build #12759, SR is auto attack only.
			if (mState.mSavageRoarEv.isUp()) {
				mSRWhiteDamage += Math.round((damage * mState.mModel.multRoar) - damage);
				damage *= mState.mSavageRoarEv.isUp() ? mState.mModel.multRoar : 1;
			}
			stats.mWhiteDamage += Math.round(damage);

			if (mState.mIsLogging) mState.mLog.log("White Swing: %s %s Damage: %d\n", targetName, result, Math.round(damage));

			if (result == AttackResult.CRIT) mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.WHITECRIT); else mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.WHITE);

			mState.mOocEv.run();

			if (mState.mFurySwipesCooldownExpireTime <= mState.mSimEngine.getSimTime()) {
				if (mState.mSimEngine.getRandom().nextBoolean(mState.mModel.pFurySwipes)) {
					result = (mState.mActiveTargetData.mCanParry) ? mState.mModel.getFrontalYellowResult() : mState.mModel.getYellowResult();

					if (result == AttackResult.MISS) {
						stats.mNrFurySwipesMiss++;

						if (mState.mIsLogging) mState.mLog.log("Fury Swipes: %s MISS\n", targetName);
					} else if (result == AttackResult.DODGE) {
						stats.mNrFurySwipesDodge++;

						if (mState.mIsLogging) mState.mLog.log("Fury Swipes: %s DODGE\n", targetName);
					} else if (result == AttackResult.PARRY) {
						stats.mNrFurySwipesParry++;

						if (mState.mIsLogging) mState.mLog.log("Fury Swipes: %s PARRY\n", targetName);
					} else {
						// BUG: Does not benefit from Bonus Weapon Damage (4.0.6 PTR)
						damage = mState.mModel.getWeaponDamage(true) * mState.mModel.multFurySwipes;

						if (result == AttackResult.CRIT) {
							stats.mNrFurySwipesCrit++;
							damage *= mState.mModel.multCrit;
						} else {
							stats.mNrFurySwipesHit++;
						}

						damage *= mState.getDirectDamageMult();
						stats.mFurySwipesDamage += Math.round(damage);

						if (mState.mIsLogging) mState.mLog.log("Fury Swipes: %s %s Damage: %d\n", targetName, result, Math.round(damage));

						if (result == AttackResult.CRIT) mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.YELLOWCRIT); else mState.mSimEngine.triggerProc(mState.mActiveTarget, Trigger.YELLOW);
					}

					mState.mFurySwipesCooldownExpireTime = mState.mSimEngine.getSimTime() + mState.mModel.furySwipesCDDuration;
				}
			}
		}

		setTickInterval(mState.mModel.getMeleeInterval());
	}

	public void start() {
		// Only start the swing timer if we can actually attack the mob.
		if (!isUp() && mState.isInMeleeRange() && !mState.mRebirthEv.isCasting() && !mState.mTranquilityEv.isCasting()) {
			schedule(Double.MAX_VALUE, mState.mModel.getMeleeInterval());
		}
	}

	public void stop() {
		cancel();
	}

	public void reset() {
		schedule(Double.MAX_VALUE, mState.mModel.getMeleeInterval());
	}

	long mSRWhiteDamage;
}
